/*
 * Copyright 2015 CPE Lyon - IRC 2013/2016 - Cours de programmation réseau - Groupe H
 *
 * This file is part of BOTT.
 *
 * BOTT is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 *
 * BOTT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with BOTT. If not, see http://www.gnu.org/licenses/.
 */

/*
 * Fonctions dédiées au serveur de messagerie.
 */

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <pthread.h>
#include "protocole.h"
#include "serveur.h"
#include "fonctions.h"
#include "serveur.h"
#include "traitements-serveur.h"
#include "fonctions-serveur.h"

#define SETTINGS "settings.conf"
#define MAX_CLIENTS "max_clients"
#define MAX_CHATS "max_chats"
#define DEBUG_MODE "debug_mode"

/* Initialisation du serveur */
void init(client *listClients, chat *listChats) {
    /* Détournement du signal pour l'arret */
    signal(SIGUSR1, stopServer);
    signal(SIGINT, stopServer);
    /* Initialiser les listes */
    client emptyClient;
    emptyClient.idUser = 0;
    for (int i = 0; i < getMaxClientsNumber()*getMaxChatsNumber(); i++) {
        listClients[i] = emptyClient;
    }
    chat emptyChat;
    emptyChat.idChat = 0;
    for (int j = 0; j < getMaxChatsNumber(); j++) {
        listChats[j] = emptyChat;
    }
}

/* Fonction d'arrêt du serveur */
void stopServer() {
    printf("%s\e[1m< BOTT server is stopping >\t\n", KRED);
    clearColors();
    exit(0);
}

/* Obtenir le nombre maximum de clients autorisés */
int getMaxClientsNumber() {
    int configuration;
    int maxClients = -1;
    configuration = open(SETTINGS, O_RDONLY);
    if (configuration == -1) {
        perror("Open file error");
        exit(1);
    }
    char buffer[BUFFER];
    memset(buffer, 0, BUFFER);
    size_t sizeRead;
    char subBuffer[3];
    while((sizeRead = read(configuration, buffer, BUFFER)) > 0) {
        char *p = strstr(buffer, MAX_CLIENTS);
        if (p != NULL) {
            memcpy(subBuffer, &p[strlen(MAX_CLIENTS) + 4], 2);
            subBuffer[2] = '\0';
            maxClients = atoi(subBuffer);
        }
    }
    close(configuration);
    return maxClients;
}

/* Obtenir le nombre maximum de salons autorisés */
int getMaxChatsNumber() {
    int configuration;
    int maxChats = -1;
    configuration = open(SETTINGS, O_RDONLY);
    if (configuration == -1) {
        perror("Open file error");
        exit(1);
    }
    char buffer[BUFFER];
    memset(buffer, 0, BUFFER);
    size_t sizeRead;
    char subBuffer[3];
    while((sizeRead = read(configuration, buffer, BUFFER)) > 0) {
        char *p = strstr(buffer, MAX_CHATS);
        if (p != NULL) {
            memcpy(subBuffer, &p[strlen(MAX_CHATS) + 4], 2);
            subBuffer[2] = '\0';
            maxChats = atoi(subBuffer);
        }
    }
    close(configuration);
    return maxChats;
}


/* Savoir si le mode de débug est activé */
int isDebugActivated() {
    int configuration;
    int debug = 0;
    configuration = open(SETTINGS, O_RDONLY);
    if (configuration == -1) {
        perror("Open file error");
        exit(1);
    }
    char buffer[BUFFER];
    memset(buffer, 0, BUFFER);
    size_t sizeRead;
    char subBuffer[6];
    while((sizeRead = read(configuration, buffer, BUFFER)) > 0) {
        char *p = strstr(buffer, DEBUG_MODE);
        if (p != NULL) {
            memcpy(subBuffer, &p[strlen(DEBUG_MODE) + 3], 5);
            subBuffer[5] = '\0';
            if (strncmp(subBuffer, "true", 4) == 0) {
                debug = 1;
            }
        }
    }
    close(configuration);
    return debug;
}

/* Obtenir le prochain ID client */
int getNextClientId(client *listClients) {
    int nextId = 1;
    int maxClients = getMaxClientsNumber() * getMaxChatsNumber();
    for (int i = 0; i < maxClients; i++) {
        if (listClients[i].idUser != 0) {
            nextId++;
        }
    }
    if (nextId > maxClients) {
            nextId = 0;
    }
    return nextId;
}

/* Obtenir le prochain ID de salon */
int getNextChatId(chat *listChats) {
    int nextId = 1;
    int maxChats = getMaxChatsNumber();
    for (int i = 0; i < maxChats; i++) {
        if (listChats[i].idChat != 0) {
            nextId++;
        }
    }
    if (nextId > maxChats) {
            nextId = 0;
    }
    return nextId;
}

/* Obtenir le prochain index de client */
int getNextClientIndex(client *listClients) {
    int nextIndex = -1;
    int maxClients = getMaxClientsNumber() * getMaxChatsNumber();
    for (int i = 0; i < maxClients; i++) {
        if (listClients[i].idUser    == 0) {
            nextIndex = i;
        }
    }
    return nextIndex;
}

/* Obtenir le prochain index de salon */
int getNextChatIndex(chat *listChats) {
    int nextIndex = -1;
    int maxChats = getMaxChatsNumber();
    for (int i = 0; i < maxChats; i++) {
        if (listChats[i].idChat == 0) {
            nextIndex = i;
        }
    }
    return nextIndex;
}

/* Créer une structure pour l'envoi via le socket */
struct sockaddr_in getSendStruct(struct in_addr ip, int16_t port) {
    struct sockaddr_in sendAddr;
    sendAddr.sin_family = AF_INET;
    sendAddr.sin_port = port;
    sendAddr.sin_addr = ip;
    return sendAddr;
}

/* Récupérer l'IP d'un client */
struct in_addr getUserIp(client *listClients, int idUser) {
    struct in_addr ip = { 0 };
    for (int i = 0; i < getMaxClientsNumber() * getMaxChatsNumber(); i++) {
        if (listClients[i].idUser == idUser) {
            ip = listClients[i].ipUser;
        }
    }
    return ip;
}

/* Récupérer le port associé à un client */
int16_t getUserPort(client *listClients, int idUser) {
    int16_t port = 0;
    for (int i = 0; i < getMaxClientsNumber() * getMaxChatsNumber(); i++) {
        if (listClients[i].idUser == idUser) {
            port = listClients[i].portUser;
        }
    }
    return port;
}

/* Obtenir l'index d'un salon dans la liste */
int getChatIndex(chat *listChats, char *chatName) {
    int index = -1;
    for (int i = 0; i < getMaxChatsNumber(); i++) {
        if (strcmp(listChats[i].chatName, chatName) == 0) {
            index = i;
        }
    }
    return index;
}

/* Obtenir l'index d'un salon dans la liste depuis son ID */
int getChatIndexFromId(chat *listChats, int idChat) {
    int index = -1;
    for (int i = 0; i < getMaxChatsNumber(); i++) {
        if (listChats[i].idChat == idChat) {
            index = i;
        }
    }
    return index;
}

/* Obtenir l'index d'un utilisateur dans la liste depuis son ID */
int getUserIndexFromId(client *listClients, int idUser) {
    int index = -1;
    for (int i = 0; i < getMaxClientsNumber() * getMaxChatsNumber(); i++) {
        if (listClients[i].idUser == idUser) {
            index = i;
        }
    }
    return index;
}

/* Savoir si un client est connecté */
int isClientConnected(client *listClients, int idUser) {
    int connected = 0;
    int maxClients = getMaxClientsNumber() * getMaxChatsNumber();
    for (int i = 0; i < maxClients; i++) {
        if (listClients[i].idUser == idUser) {
            connected = 1;
            break;
        }
    }
    return connected;
}

/* Savoir si un client est dans un salon */
int isClientInChat(chat *listChats, int idUser, int idChat) {
    int connected = 0;
    int maxClients = getMaxClientsNumber();
    for (int i = 0; i < maxClients; i++) {
        if (connected) {
            break;
        }
        if (listChats[i].idChat == idChat) {
            for (int j = 0; j < MAX; j++) {
                if (listChats[i].listClients[j] == idUser) {
                    connected = 1;
                    break;
                }
            }
        }
    }
    return connected;
}

/* Ajouter un client dans la liste des clients d'un salon */
int addClientInChat(int idClient, int idChat, chat *allChats) {
    int full = 1;
    int index = getChatIndexFromId(allChats, idChat);
    for (int i = 0; i < getMaxClientsNumber(); i++) {
        if (allChats[index].listClients[i] == 0) {
            allChats[index].listClients[i] = idClient;
            full = 0;
            break;
        }
    }
    return full;
}

/* Ajouter un salon dans la liste des salons d'un client */
int addChatInClient(int idClient, int idChat, client *allClients) {
    int full = 1;
    int index = getUserIndexFromId(allClients, idClient);
    for (int i = 0; i < getMaxClientsNumber() * getMaxChatsNumber(); i++) {
        if (allClients[index].listChats[i] == 0) {
            allClients[index].listChats[i] = idChat;
            full = 0;
            break;
        }
    }
    return full;
}

/* Supprimer un client dans la liste des clients d'un salon */
void deleteClientInChat(int idClient, int idChat, chat *allChats) {
    int index = getChatIndexFromId(allChats, idChat);
    for (int i = 0; i < getMaxClientsNumber(); i++) {
        if (allChats[index].listClients[i] == idClient) {
            allChats[index].listClients[i] = 0;
        }
    }
}

/* Supprimer un salon dans la liste des salons d'un client */
void deleteChatInClient(int idClient, int idChat, client *allClients) {
    int index = getUserIndexFromId(allClients, idClient);
    for (int i = 0; i < getMaxClientsNumber() * getMaxChatsNumber(); i++) {
        if (allClients[index].listChats[i] == idChat) {
            allClients[index].listChats[i] = 0;
        }
    }
}

/* Fonction de débug */
void debug_displayListClients(client *listClients) {
    printf("Client list\n");
    int maxClients = getMaxClientsNumber() * getMaxChatsNumber();
    for (int i = 0; i < maxClients; i++) {
        if (listClients[i].idUser != 0) {
            printf("[%d] ID : %d - Username : %s\n", i, listClients[i].idUser, listClients[i].nickname);
            printf("\tChats :");
            for (int j = 0; j < MAX; j++) {
                if (listClients[i].listChats[j] != 0) {
                    printf(" %d", listClients[i].listChats[j]);
                }
            }
            printf("\n");
        } else {
            printf("[%d] Vide\n", i);
        }
    }
}

/* Fonction de débug */
void debug_displayListChats(chat *listChats) {
    printf("Chats list\n");
    int maxChats= getMaxChatsNumber();
    for (int i = 0; i < maxChats; i++) {
        if (listChats[i].idChat != 0) {
            printf("[%d] ID : %d - Chat name : %s\n", i, listChats[i].idChat, listChats[i].chatName);
            printf("\tClients :");
            for (int j = 0; j < MAX; j++) {
                if (listChats[i].listClients[j] != 0) {
                    printf(" %d", listChats[i].listClients[j]);
                }
            }
            printf("\n");
        } else {
            printf("[%d] Vide\n", i);
        }
    }
}